{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# <center>Анализ пользователей инстаграма по признаку бизнес-аккаунта\n",
    "<center> Автор: Петр Иванов"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Python 2 and 3 compatibility\n",
    "from __future__ import (absolute_import, division,\n",
    "                        print_function, unicode_literals)\n",
    "# отключим предупреждения Anaconda\n",
    "import warnings\n",
    "warnings.simplefilter('ignore')\n",
    "import pandas as pd\n",
    "%pylab inline\n",
    "import seaborn as sns\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.tree import DecisionTreeClassifier \n",
    "from sklearn.ensemble import RandomForestClassifier\n",
    "from sklearn.ensemble import AdaBoostClassifier\n",
    "from sklearn.ensemble import GradientBoostingClassifier\n",
    "from sklearn.ensemble import ExtraTreesClassifier\n",
    "from sklearn.ensemble import ExtraTreesRegressor\n",
    "from sklearn.ensemble import AdaBoostRegressor"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.cross_validation import cross_val_score\n",
    "import numpy as np "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.metrics import accuracy_score\n",
    "from sklearn.metrics import roc_auc_score\n",
    "from sklearn.metrics import confusion_matrix, f1_score"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Описание набора данных и признаков (на понятном русском, желательно с картинками) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Описание\n",
    "Данный проект решает задачу нахождения (классификации) коммерческих аккаунтов в сети Инстаграм по публично доступным данным. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Персональный аккаунт:\n",
    "<img src=\"../../img/personal_acc.png\">\n",
    "\n",
    "Коммерческий аккаунт:\n",
    "<img src=\"../../img/business_acc.png\">\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Данные\n",
    "Набор данных представляет собой CSV файл с 1348 записями. Данные взяты преимущественно по русскоязычному сегменту сети. \n",
    "\n",
    "<li>IG_USER_ID - ID из SQL базы\n",
    "<li>USER_ID - внутренний ID инстаграмма в текстовом виде. Узнать его можно тут например: http://jelled.com/instagram/lookup-user-id\n",
    "<li>USER_ID_INT - он же в числовом виде\n",
    "<li>IG_USER_NAME - имя пользователя в инстаграме\n",
    "<li>F_MEDIA_NOW - количество фото и видео на близкий к текущему момент времени\n",
    "<li>F_FOLLOWERS_NOW - количество фолловеров(подписчиков) на близкий к текущему момент времени\n",
    "<li>F_FOLLOWING_NOW - количество подписок на других пользователей к текущему момент времени\n",
    "<li>F_ISPRIVATE_NOW - является ли аккаунт закрытым на близкий к текущему момент времени\n",
    "<li>F_IS_BUSINESS - метка коммерческого аккаунта (размечена руками) - я буду работать с ней\n",
    "<li>F_IS_MALE - метка мужского аккаунта (размечена руками)\n",
    "<li>F_IS_FEMALE - метка женского аккаунта (размечена руками) \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. Первичный анализ признаков"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ig_df =  pd.read_csv('../../data/ig_users.csv')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ig_df = ig_df.set_index('IG_USER_ID') #задействуем родной id из базы "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ig_df = ig_df.drop('USER_ID', 1) #оставим только один ID "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ig_df.head(3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ig_df.describe(include = \"all\").T"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "В выборке у нас пропусков нет, 60% коммерческих аккаунтов, 12% мужчин и 27% женщин. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#выделим признаки и аттрибуты\n",
    "X = ig_df.loc[:,:'F_ISPRIVATE_NOW']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "X = X.drop('IG_USER_NAME',1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Y_biz = ig_df['F_IS_BUSINESS'] "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Я считаю родной ID признаком, потому что он косвенно говорит о времени создания аккаунта. И порядок в нём имеет значение. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Первичный визуальный анализ признаков "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sns.pairplot(ig_df.loc[:,:'F_IS_BUSINESS'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Чёткие прямоугольники в районе Following получаются, потому что ИГ не даёт аккаунтам следовать больше чем за 8000 других аккаунтов. Но когда-то было не так, оттуда остались всплески. Количество подписчиков и медиа распределены по степенному закону, но в случае подписок он немного другой и дальше мы увидим, что это не случайно."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. если пришли к каким-то \"инсайтам\", обнаружили закономерности - изложите (это самое главное) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sns.pairplot(ig_df[['F_FOLLOWERS_NOW','USER_ID_INT']])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Тут на графике виден сходящийся к нулю треугольник. Чем моложе аккаунт, тем меньше у него постов. Но где-то начиная с середины истории (условно 2013 год) стали появляться другие аккаунты. Поэтому-то внутренний ID ИГ - это важный признак.   "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sns.pairplot(ig_df.loc[:,:'F_IS_BUSINESS'],hue=\"F_IS_BUSINESS\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "при более пристальном анализе видно, что бизнес-аккаунты сильно зависят от ID и от FOLLOWING. Фактически большинство новых аккаунтов - это коммерческие аккаунты. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sns.pairplot(ig_df[['USER_ID_INT','F_FOLLOWING_NOW','F_IS_BUSINESS']],hue=\"F_IS_BUSINESS\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### обратите внимание на гистограммы по диагонали."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "g = sns.FacetGrid(ig_df[['USER_ID_INT','F_IS_BUSINESS']], hue=\"F_IS_BUSINESS\", size=4, aspect=1.3)\n",
    "g.map(plt.hist, \"USER_ID_INT\", histtype='barstacked', stacked=True);\n",
    "plt.legend();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def stackhist(x, y, **kws):\n",
    "    grouped = pd.groupby(x, y)\n",
    "    data = [d for _, d in grouped]\n",
    "    labels = [l for l, _ in grouped]\n",
    "    plt.hist(data, histtype=\"barstacked\", label=labels)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "stackhist(ig_df.USER_ID_INT, ig_df.F_IS_BUSINESS)\n",
    "plt.title(\"User account age and commerce\")\n",
    "plt.legend()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### это гистограмма по USER_ID_INT от меньшего к большему, т.е. от прошлого в будущее. Фактически коммерческие аккаунты сейчас являются основным источноком роста социальнос"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. Оценим важность признаков"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "X.head(3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "forest = RandomForestClassifier(n_estimators=40)\n",
    "\n",
    "forest.fit(X,Y_biz)\n",
    "\n",
    "importances = forest.feature_importances_\n",
    "std = np.std([tree.feature_importances_ for tree in forest.estimators_],\n",
    "             axis=0)\n",
    "indices = np.argsort(importances)[::-1]\n",
    "\n",
    "# Print the feature ranking\n",
    "print(\"Feature ranking:\")\n",
    "\n",
    "for f in range(X.shape[1]):\n",
    "    print(\"%d. feature %s (%f)\" % (f + 1, X.columns[indices[f]], importances[indices[f]]))\n",
    "\n",
    "# Plot the feature importances of the forest\n",
    "plt.figure()\n",
    "plt.title(\"Feature importances\")\n",
    "plt.bar(range(X.shape[1]), importances[indices],\n",
    "       color=\"r\", yerr=std[indices], align=\"center\")\n",
    "plt.xticks(range(X.shape[1]), indices)\n",
    "plt.xlim([-1, X.shape[1]])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Как и ожидалось ID оказалось очень важным признаком"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6. Кросс-валидация"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.cross_validation import train_test_split\n",
    "X_train, X_test, y_train, y_test = train_test_split(X, Y_biz, \n",
    "                                                    test_size=0.1, \n",
    "                                                    random_state=42,\n",
    "                                                   stratify = Y_biz)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "forest = RandomForestClassifier(n_estimators=500)\n",
    "forest.fit(X_train, y_train)\n",
    "test_pred = forest.predict(X_test)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "accuracy_score(y_test, test_pred)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "y_test.value_counts()[0] / y_test.shape[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "в данном кейсе случайное предсказание работает гораздо хуже леса "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f1_score(y_test, test_pred)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "И f1 нам намекает, что мы на правильном пути"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.tree import DecisionTreeClassifier # Ваш код здесь\n",
    "from sklearn.ensemble import RandomForestClassifier\n",
    "from sklearn.ensemble import AdaBoostClassifier\n",
    "from sklearn.ensemble import GradientBoostingClassifier\n",
    "from sklearn.ensemble import ExtraTreesClassifier"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.cross_validation import cross_val_score\n",
    "import numpy as np \n",
    "from sklearn.grid_search import GridSearchCV"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "classifiers = {'st': DecisionTreeClassifier(),'rf': RandomForestClassifier(),'ada_c':AdaBoostClassifier(), \\\n",
    "              'gb_c':GradientBoostingClassifier(), 'ext_c':ExtraTreesClassifier()}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tree_params = {'min_samples_split' : list(range(1,3)),\n",
    "               'max_depth': list(range(6,10)), \n",
    "               'min_samples_leaf': list(range(1,3))}\n",
    "\n",
    "AdaBoost_params = {'n_estimators': (50,100,150)}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "classifiers = {'st': GridSearchCV(DecisionTreeClassifier(), tree_params, verbose=True, n_jobs=-1, cv=5),\\\n",
    "               'rf': GridSearchCV(RandomForestClassifier(), tree_params, verbose=True, n_jobs=-1, cv=5),\\\n",
    "               'ada_c':GridSearchCV(AdaBoostClassifier(), AdaBoost_params, verbose=True, n_jobs=-1, cv=5), \\\n",
    "              'gb_c':GridSearchCV(GradientBoostingClassifier(), tree_params, verbose=True, n_jobs=-1, cv=5),\\\n",
    "               'ext_c':GridSearchCV(ExtraTreesClassifier(), tree_params, verbose=True, n_jobs=-1, cv=5)} "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "clf_scores= {}\n",
    "\n",
    "for my_clf in classifiers:\n",
    "    classifiers[my_clf].fit(X_train,y_train)\n",
    "    y_pred = classifiers[my_clf].predict(X_test)\n",
    "    clf_scores[my_clf] = accuracy_score(y_test.as_matrix(), y_pred)\n",
    "    print (\"The mean %(tree)s is %(mean).3f\" % {'tree': my_clf, 'mean' : clf_scores[my_clf]})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import operator\n",
    "best_clf  = max(clf_scores.iteritems(), key=operator.itemgetter(1))[0]\n",
    "print (\"The best is %(tree)s with %(mean).3f with param %(par)s\" % {'tree':best_clf, 'mean' : clf_scores[best_clf],\n",
    "                                                                   'par':classifiers[best_clf].best_params_})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Лучший у нас оказался случайный лес"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "rf_tree_params = {'n_estimators': (250, 300, 350),\n",
    "                  'min_samples_split' : list(range(1,5)),\n",
    "               'max_depth': list(range(6,14)), \n",
    "               'min_samples_leaf': list(range(1,5))}\n",
    "n_folds = 5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "clf = RandomForestClassifier()\n",
    "\n",
    "grid = GridSearchCV(clf, rf_tree_params, cv=n_folds, \n",
    "                    n_jobs=-1, scoring=\"f1\")\n",
    "grid.fit(X_train, y_train)\n",
    "clf_best_score = grid.best_score_\n",
    "clf_best_params = grid.best_params_\n",
    "clf_best = grid.best_estimator_\n",
    "mean_validation_scores = []\n",
    "print(\"Лучший результат\", clf_best_score, \n",
    "      \"лучшие параметры\", clf_best_params)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7. Проверяем сходимость модели"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.learning_curve import learning_curve"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_with_std(x, data, **kwargs):\n",
    "        mu, std = data.mean(1), data.std(1)\n",
    "        lines = plt.plot(x, mu, '-', **kwargs)\n",
    "        plt.fill_between(x, mu - std, mu + std, edgecolor='none',\n",
    "                         facecolor=lines[0].get_color(), alpha=0.2)\n",
    "        \n",
    "def plot_learning_curve(clf, X, y, scoring, cv=5):\n",
    " \n",
    "    train_sizes = np.linspace(0.05, 1, 20)\n",
    "    n_train, val_train, val_test = learning_curve(clf,\n",
    "                                                  X, y, train_sizes, cv=cv,\n",
    "                                                  scoring=scoring)\n",
    "    plot_with_std(n_train, val_train, label='training scores', c='green')\n",
    "    plot_with_std(n_train, val_test, label='validation scores', c='red')\n",
    "    plt.xlabel('Training Set Size'); plt.ylabel(scoring)\n",
    "    plt.legend()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot_learning_curve(RandomForestClassifier(min_samples_split=2, n_estimators=350, max_depth=7, min_samples_leaf=4),\n",
    "                   X_train, y_train, scoring='f1', cv=10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot_learning_curve(RandomForestClassifier(min_samples_split=2, n_estimators=350, max_depth=7, min_samples_leaf=4),\n",
    "                   X_train, y_train, scoring='roc_auc', cv=10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot_learning_curve(RandomForestClassifier(min_samples_split=2, n_estimators=350, max_depth=7, min_samples_leaf=4),\n",
    "                   X_train, y_train, scoring='accuracy', cv=10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Согласно F1 данных достаточно, но ROC и Accuracy намекают, что данных можно было бы взять ещё. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Бустинг"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.learning_curve import validation_curve\n",
    "\n",
    "def plot_validation_curve(clf, X, y, cv_param_name, \n",
    "                          cv_param_values, scoring):\n",
    "\n",
    "    val_train, val_test = validation_curve(clf, X, y, cv_param_name,\n",
    "                                           cv_param_values, cv=5,\n",
    "                                                  scoring=scoring)\n",
    "    plot_with_std(cv_param_values, val_train, \n",
    "                  label='training scores', c='green')\n",
    "    plot_with_std(cv_param_values, val_test, \n",
    "                  label='validation scores', c='red')\n",
    "    plt.xlabel(cv_param_name); plt.ylabel(scoring)\n",
    "    plt.legend()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "estimators = np.arange(25, 350, 25)\n",
    "plot_validation_curve(RandomForestClassifier(min_samples_split=2, \n",
    "                                             max_depth=7, min_samples_leaf=4), X_train, y_train, \n",
    "                      cv_param_name='n_estimators', \n",
    "                      cv_param_values= estimators,\n",
    "                   scoring='roc_auc')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Видно, что количество деревьев не влияет. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "depth = np.arange(3, 25)\n",
    "plot_validation_curve(RandomForestClassifier(min_samples_split=2, n_estimators = 350\n",
    "                                             , min_samples_leaf=4), X_train, y_train, \n",
    "                      cv_param_name='max_depth', \n",
    "                      cv_param_values= depth,\n",
    "                   scoring='roc_auc')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Больше 10 делать глубину нет смысла. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "final_forest = RandomForestClassifier(min_samples_split=2, n_estimators=350, max_depth=7, min_samples_leaf=4)\n",
    "final_forest.fit(X_train, y_train)\n",
    "final_pred = final_forest.predict(X_test)\n",
    "accuracy_score(y_test, final_pred), f1_score(y_test, final_pred),roc_auc_score(y_test, final_pred)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Выводы"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Построена модель классификации коммерческих аккаунтов в социальной сети Инстаграмм. Данная модель даёт примерно 90% точности на отложенных 10% выборки. Данный результат устойив к способу измерения погрешности, а именно достижим на метриках Accuracy, F1, ROC_AUC. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Были построены валидационные кривые, которые говорят нам о том, что данных в количестве 1300 штук нам немного не достаёт. Сходимость дальше есть, можно собрать как минимум столько же.  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Глубина 7 найденная жадным поиском в принципе оптимальна. Дальше модель получается слегка переобучена. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Главный инсайт в том, что данная социальная сеть продолжает свой рост в основном за счёт коммерческих аккаунтов, а не частных. Рост частных сходит на нет, а рост коммеррческих набирает обороты. А так же в том, что не заглядывая в фотографии и не читая текстов по формальным признакам можно отсекать спам в ИГ с хорошей вероятностью. Недостатком метода является то, что модели и способы работы коммерческих аккаунтов или паттерны поведения частных лиц могут существенно поменяться со временем и данная модель потеряет актуальность. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Результаты данного исследования можно проверить любому желающему, использованные данные общедоступны."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### P.S. данная модель достоверна для русского сегмента. Выборка случайна только в пределах РФ. "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
